<?php
// Copyright 2019 Hackware SpA <human@hackware.cl>
// This file is part of "Hackware Web Services Payment" and licensed under
// the terms of the GNU Affero General Public License version 3, or (at your
// option) a later version. You should have received a copy of this license
// along with the software. If not, see <https://www.gnu.org/licenses/>.

namespace Hawese\Payment\Gateways;

use Hawese\Payment\Payment;
use Hawese\Payment\Exceptions\UnexpectedResponseException;
use Hawese\Payment\Support\Http as HttpSupport;
use Illuminate\Support\{Arr, Str};
use Psr\Http\Message\ResponseInterface;

class KhipuGateway extends AbstractGateway
{
    public function purchase(array $bodyParams): array
    {
        // payment_method is processed on this side
        $payment_method = Arr::pull($bodyParams, 'payment_method');

        $bodyParams = $this->purchaseAddDefaultParams($bodyParams);
        $response = $this->request('POST', '/payments', $bodyParams);
        $responseBody = $this->purchaseProcessResponse($response);

        $this->purchaseInsertPayment(
            $bodyParams,
            $responseBody,
            $payment_method
        );

        return $this->purchaseReturnResponse($responseBody, $payment_method);
    }

    private function purchaseAddDefaultParams(array $bodyParams): array
    {
        $uuid = (string) Str::uuid();
        $returnUrl = HttpSupport::appendQueryParams(
            config('payment.return_url'),
            ['uuid' => $uuid]
        );

        return array_merge($bodyParams, [
            'transaction_id' => $uuid,
            'currency' => 'CLP',
            'return_url' => $returnUrl,
            'cancel_url' => $returnUrl,
            'notify_url' => url(route(
                'gateways.notify',
                ['gateway' => 'khipu'],
            )),
        ]);
    }

    /**
     * Validate and process $response
     * @returns object parsed body
     */
    private function purchaseProcessResponse(
        ResponseInterface &$response
    ): object {
        if ($response->getStatusCode() == 201) {
            $responseBody = HttpSupport::getJsonBody($response);

            if (property_exists($responseBody, 'payment_id')) {
                return $responseBody;
            }
        }

        throw new UnexpectedResponseException($response);
    }

    /**
     * @returns string Payment primary key value
     */
    private function purchaseInsertPayment(
        array &$bodyParams,
        object &$responseBody,
        string &$payment_method
    ): string {
        return (new Payment([
            'uuid' => $bodyParams['transaction_id'],
            'user_uid' => $bodyParams['payer_email'],
            'gateway' => 'khipu',
            'currency' => $bodyParams['currency'],
            'amount' => $bodyParams['amount'], // Integers only!!
            'description' => $bodyParams['subject'],
            'detail' => json_encode([
                'payment_id' => $responseBody->payment_id,
                'payment_method' => $payment_method,
            ]),
            'status' => Payment::STATUS_PENDING,
        ]))->insert();
    }

    private function purchaseReturnResponse(
        object &$responseBody,
        string &$payment_method
    ): array {
        switch ($payment_method) {
            case 'transfer':
                $redirectTo = 'transfer_url';
                break;
            case 'simplified_transfer':
                $redirectTo = 'simplified_transfer_url';
                break;
            default:
                $redirectTo = 'payment_url';
        }

        return ['redirect_url' => $responseBody->{$redirectTo}];
    }

    public function acceptNotification(array $bodyParams)
    {
        $response = $this->request(
            'GET',
            '/payments?' . HttpSupport::buildUriQuery([
                'notification_token' => $bodyParams['notification_token'],
            ])
        );
        $responseBody = HttpSupport::getJsonBody($response);

        $payment = Payment::find($responseBody->transaction_id);

        if ($payment->status === Payment::STATUS_COMPLETED) {
            return response('Already processed', 400);
        } else {
            $payment->validateAndUpdateStatus(
                $responseBody->status_detail,
                ['normal', 'marked-paid-by-receiver'],
                ['pending'],
                $responseBody->status === 'done'
            );
            if ($payment->status === Payment::STATUS_COMPLETED) {
                return response('Well done Khipu!', 200);
            }
        }
    }
}
